﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;

namespace dotNetInteropPlugin.CSharpMixingLibrary
{
    [Serializable]
    internal struct PluginShortInfoForLoad
    {
        public string AssemblyFileName;
        public string AssemblyFullName;
        public string ClassName;
        public PluginDev.AIMPManagedPluginAttribute PluginLocInfo;
    }

    interface IPluginLoadingStrategy
    {
        PluginShortInfoForLoad[] Load(string Path);
    }

    internal abstract class PluginLoadingStrategy : MarshalByRefObject//, IPluginLoadingStrategy
    {
        public abstract PluginShortInfoForLoad[] Load(string Path);
    }

    internal class AssemblyScanPluginLoadStrategy : PluginLoadingStrategy
    {
        private IEnumerable<FileInfo> ScanFiles(DirectoryInfo di, int Depth)
        {
            foreach (FileInfo finf in di.GetFiles("*.dll"))
                yield return finf;

            if (Depth > 0)
            {
                foreach (DirectoryInfo dinf in di.GetDirectories())
                    foreach (FileInfo finf in ScanFiles(dinf, Depth - 1))
                        yield return finf;
            }
        }

        public override PluginShortInfoForLoad[] Load(string Path)
        {
            List<PluginShortInfoForLoad> resPlugInfolst = new List<PluginShortInfoForLoad>();

            DirectoryInfo di = new DirectoryInfo(Path);
            Assembly curAsmbl = null;
            Type pluginDeriveType = Type.GetType("dotNetInteropPlugin.PluginDev.MAIMPManagedPlugin"); //typeof(PluginDev.MAIMPManagedPlugin);
            Type attribForPlugin = typeof(PluginDev.AIMPManagedPluginAttribute);
            PluginDev.AIMPManagedPluginAttribute curAttr = null;

            if (di.Exists)
            {
                foreach (FileInfo finf in ScanFiles(di, 0))
                {
                    try
                    {
                        curAsmbl = Assembly.LoadFrom(finf.FullName);
                        if (curAsmbl.FullName != Assembly.GetExecutingAssembly().FullName)
                        {
                            foreach (Type PlgType in curAsmbl.GetTypes().Where(o => o.IsSubclassOf(pluginDeriveType) && o.GetCustomAttributes(attribForPlugin, false).Length == 1))
                            {
                                curAttr = (PluginDev.AIMPManagedPluginAttribute)PlgType.GetCustomAttributes(attribForPlugin, false)[0];
                               // if (!curAttr.RequireAppDomain || PlgType.IsSubclassOf(typeof(MarshalByRefObject)))
                                resPlugInfolst.Add(new PluginShortInfoForLoad() { AssemblyFileName = finf.FullName, AssemblyFullName = curAsmbl.FullName, ClassName = PlgType.FullName, PluginLocInfo = curAttr });
                            }
                        }
                    }
                    catch { }
                }
            }

            return resPlugInfolst.ToArray();
        }
    }


    internal static class PluginInfoLoader
    {
        public static Type LoadStrategyType = typeof(AssemblyScanPluginLoadStrategy);

        public static void LoadPlugins(string Path, PluginInfoCollection plgCol)
        {
            AppDomain loadDomain = null;

            try
            {
                AppDomainSetup domainSet = new AppDomainSetup();
                domainSet.ApplicationBase = Path;
                loadDomain = AppDomain.CreateDomain("PluginLoadDomain" + new Guid().ToString().GetHashCode().ToString("x"), null, domainSet);

                PluginLoadingStrategy strat = (PluginLoadingStrategy)loadDomain.CreateInstanceAndUnwrap(Assembly.GetExecutingAssembly().FullName, LoadStrategyType.FullName);

                PluginShortInfoForLoad[] res = strat.Load(Path);
                foreach (PluginShortInfoForLoad cInf in res)
                    plgCol.Add(new PluginInformation(cInf.AssemblyFileName, cInf.AssemblyFullName, cInf.ClassName, cInf.PluginLocInfo));
            }
#if DEBUG
            catch(Exception e)
            {
                System.Windows.Forms.MessageBox.Show(e.Message);
            }
#endif
            finally
            {
                if (loadDomain != null)
                    AppDomain.Unload(loadDomain);
            }
        }

        public static PluginInfoCollection LoadPlugins(string Path)
        {
            PluginInfoCollection plgCol = new PluginInfoCollection();
            LoadPlugins(Path, plgCol);
            return plgCol;
        }
    }
}
